﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

namespace Amie
{
   public class PreReqResults
    {
       private ProductInfo _info;
       public PreReqResults(ProductInfo info)
       {
           _info = info;
       }

       public List<CheckResult> PerformTests()
       {
           var resultList = new List<CheckResult>();
           resultList.Add(IISInstalled());
           resultList.Add(FrameworkCheck());
           resultList.Add(WebSiteFound());
           resultList.Add(WritePermissions());

           return resultList;
       }

       public ProductInfo ProductInfo
       {
           get
           {
               return _info;
           }
       }

       private CheckResult IISInstalled()
       {
           var c = new CheckResult("IIS Installed");
           c.Pass = IISHelper.IISIsInstalled();
           
           if (c.Pass)
                c.TestInformation = "Status: " +  IISHelper.IISStatus();
           return c;
       }

       private CheckResult FrameworkCheck()
       {
           var c = new CheckResult(".net Framework 4.5");
           string result = Get45or451FromRegistry();
           c.Pass = !result.StartsWith("No");
           c.TestInformation = result;
           return c;
       }

       private CheckResult WebSiteFound()
       {
           var c = new CheckResult("Web site found");
           var site = IISHelper.GetFirstSite();
           if (site != null)
           {
               c.Pass = true;
               c.TestInformation = site.Name;
           }
           else
           {
               c.Pass = false;
               c.TestInformation = "No web sites found";
           }
           return c;
       }

       private CheckResult WritePermissions()
       {
           var c = new CheckResult("Config Write Permissions");
           bool servicePermission = ServiceWritePermission();
           bool webPermission = WebWritePermission();


           if (servicePermission && webPermission)
               c.Pass = true;

           c.TestInformation = webPermission ? "Web: Yes" : "Web: No";
           c.TestInformation += servicePermission ? " Service: Yes" : " Service: No";

           return c;
       }

       private bool ServiceWritePermission()
       {
           var fileInfo = new FileInfo(_info.ServiceUpdateConfigPath);
           return IsWriteable(fileInfo);
       }

       private bool WebWritePermission()
       {
           var fileInfo = new FileInfo(_info.WebUpdateConfigPath);
           return IsWriteable(fileInfo);
       }

       private static bool IsWriteable(FileInfo configFile)
       {
           AuthorizationRuleCollection rules;
           WindowsIdentity identity;
           try
           {
               rules = configFile.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));
               identity = WindowsIdentity.GetCurrent();
           }
           catch (Exception ex)
           { //Posible UnauthorizedAccessException
               return false;
           }

           bool isAllow = false;
           string userSID = identity.User.Value;

           foreach (FileSystemAccessRule rule in rules)
           {
               if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference))
               {
                   if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.WriteExtendedAttributes) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Deny)
                       return false;
                   else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.WriteExtendedAttributes) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) ||
                       rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Allow)
                       isAllow = true;
               }
           }

           return isAllow;
       }

       private static string Get45or451FromRegistry()
       {
           using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\\"))
           {
               int releaseKey = Convert.ToInt32(ndpKey.GetValue("Release"));
               if (releaseKey > 0)
                   return CheckFor45DotVersion(releaseKey);
               else
                   return "Not Found";
           }
       }


       // Checking the version using >= will enable forward compatibility,  
       // however you should always compile your code on newer versions of 
       // the framework to ensure your app works the same. 
       private static string CheckFor45DotVersion(int releaseKey)
       {
           if ((releaseKey >= 379893))
           {
               return "4.5.2 or later";
           }
           if ((releaseKey >= 378675))
           {
               return "4.5.1 or later";
           }
           if ((releaseKey >= 378389))
           {
               return "4.5 or later";
           }
           // This line should never execute. A non-null release key should mean 
           // that 4.5 or later is installed. 
           return "No 4.5 or later version detected";
       }


       public class CheckResult
       {
           public CheckResult(string testName)
           {
               this.TestName = testName;
           }
           /// <summary>
           /// Did the test pass
           /// </summary>
           public bool Pass { get; set; }

           public string PassText
           {
               get
               {
                   return Pass ? "Pass" : "Fail";
               }
           }

           /// <summary>
           /// Additional information from running the test
           /// </summary>
           public string TestInformation { get; set; }

           /// <summary>
           /// The name of the test.
           /// </summary>
           public string TestName { get; set; }
       }
    }
}
